from __future__ import annotations
from abc import ABC
class IPage(ABC):
    pass

class Segment(object):
    def __init__(self, page: IPage|None = None, offset: int = 0, size: int = 512):
        self.__page = page
        self.__offset = offset
        self.__size = size
        self.__done = 0


    def size(self) -> int:
        return self.__size
    def page(self) -> IPage | None:
        return self.__page
    def offset(self) -> int:
        return self.__offset
    def done(self) -> int:
        return self.__done


    def is_done(self):
        return self.__done >= self.__size
    def advance(self, nbytes: int):
        self.__done += nbytes


    #def data(self) -> memoryview: TODO: IPage
    #    return self.__page.data()[self.__offset: self.__offset + self.__size]

    #def nr_of_sector(self) -> int:
    #    return self.__size / 512

    def __repr__(self):
        return f"page:{self.__page},offset:{self.__offset},size:{self.__size},done:{self.__done}"

class BioVec(object):
    class Iterator(object):
        def __init__(self, bio_vec: BioVec):
            self.__bio_vec = bio_vec
            self.__index = 0

        def __iter__(self):
            return self

        def __next__(self):
            if self.__index >= 0 and self.__index < self.__bio_vec.size():
                r = self.__bio_vec.get(self.__index)
                self.__index += 1
                return r
            else:
                raise StopIteration
        def __repr__(self):
            return "index:{},size:{}".format(self.__index, self.__bio_vec.size())

    def __init__(self, max_nr_vecs: int = 256):
        self.__array: list[Segment] = []
        self.__max_nr_vecs = max_nr_vecs

    def clear(self):
        self.__array.clear()

    def append(self, seg: Segment) -> bool:
        if self.size() >= self.__max_nr_vecs:
            raise OverflowError(f"reach the max number of vector:{self.__max_nr_vecs}")

        if seg is not None: 
            self.__array.append(seg)
            return True
        return False
         
    def get(self, index: int) -> Segment | None:
        if index >= 0 and index < self.size():
            return self.__array[index]
        return None

    def size(self) -> int:
        return len(self.__array)

    def iter(self) -> BioVec.Iterator:
        return BioVec.Iterator(self)

    def __iter__(self):
        return self.iter()
    

class Bio(object):
    class Iterator(object):
        def __init__(self, bio: Bio):
            self.__iter = bio.vector().iter()
            self.__done = 0

        def __next__(self):
            return self.__iter.__next__()

        def __iter__(self):
            return self

        def __repr__(self):
            return f"internal_iter:{self.__iter}"



    def __init__(self, start: int = 0, flags: int = 0, op: int = 0):
        self.__sector = start
        self.__vector: BioVec = BioVec()
        
        self.__flags = flags
        self.__op   = op

    def empty(self) -> bool:
        return self.vector().size() == 0

    def vector(self) -> BioVec:
        return self.__vector

    def unused_nbytes(self):
        return sum(map(lambda s: s.size() - s.done(), self.vector()))
    def used_nbytes(self):
        return sum(map(lambda s: s.done(), self.vector()))

    def is_done(self):
        return self.unused_nbytes() == 0

    def iter(self) -> Bio.Iterator:
        return Bio.Iterator(self)

    def __iter__(self):
        return self.iter()

    def __repr__(self):
        return f"bdev:{self.__bdev},first sector:{self.__sector},flag:{self.__flags}"



import unittest 

class TestBio(unittest.TestCase):
    def test_biovec(self):
        bvec = BioVec()
        self.assertEqual(0, bvec.size())

        bvec.append(Segment())
        self.assertEqual(1, bvec.size())


    def test_biovec_iter(self):
        bvec = BioVec()
        bvec.append(Segment(None, 0))
        bvec.append(Segment(None, 512))
        bvec.append(Segment(None, 1024))

        self.assertEqual(3, bvec.size())

        self.assertEqual(3*512, sum(map(lambda i: i.size(), bvec)))



    def test_bio(self):
        bio = Bio()
        self.assertTrue(bio.empty())

        bio.vector().append(Segment())

        sz = bio.used_nbytes()
        self.assertEqual(0, sz)

        sz = bio.unused_nbytes()
        self.assertEqual(512, sz)

        for seg in bio:
            for i in range(10):
                if not seg.is_done():
                    #wbytes = write(seg.data(), seg.nr_of_sector())
                    old_done = seg.done()
                    self.assertEqual(0, seg.done())
                    wbytes = 512
                    seg.advance(wbytes)
                    self.assertEqual(wbytes, seg.done() - old_done)
                else:
                    break

